/*
 * Copyright (C) <2009>
 *
 * <Rishi B Agrawal> <Sneha Hendre>
 * <rishi.b.agrawal@gmail.com> <sneha.hendre@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version->
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE->  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include "../include/ohsm.h"
#include "ohsm_u.h"


unsigned char flg_i_usr = UNSET, flg_usr = UNSET;
unsigned char flg_i_grp = UNSET, flg_grp = UNSET;
unsigned char flg_i_types = UNSET, flg_types = UNSET;

int
parseinfo (xmlDocPtr doc, xmlNodePtr cur, unsigned int *users,
	   unsigned int *groups, unsigned int *types)
{

	xmlChar *key;
	int temp, no;
	
	cur = cur->xmlChildrenNode;
	cur = cur->next;

	while (cur != NULL) {
		if ((!xmlStrcmp(cur->name, (const xmlChar *) "NR_USERS"))) {
			flg_i_usr = SET;
			key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
			temp = validate_key(key);
			if (temp == -1) {
				no = (int) xmlGetLineNo(cur);
				ohsm_debug( "Invalid value specified as NR_USERS on line: %d", no);
				return -1;
		    } else { 
	  		  ohsm_debug("Extracted value for NR_GROUPS %d", temp);
		      *users = temp;
			}
			xmlFree(key);
		}
		cur = cur->next->next;
	
		if ((!xmlStrcmp(cur->name, (const xmlChar *) "NR_GROUPS"))) {
			flg_i_grp = SET;
			key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
			temp = validate_key (key);
			if (temp == -1) {
				no = (int) xmlGetLineNo(cur);
		      	ohsm_debug("Invalid value specified as NR_GROUPS on line: %d", no);
		      	return -1;
		    } else {
	  		  ohsm_debug("Extracted value for NR_GROUPS %d", temp);
		      *groups = temp;
			}
			xmlFree(key);
		}
		cur = cur->next->next;
	
		if ((!xmlStrcmp(cur->name, (const xmlChar *) "NR_TYPES"))) {
			flg_i_types = SET;
			key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
			temp = validate_key(key);
	
			if (temp == -1) {
				no = (int) xmlGetLineNo(cur);
				ohsm_debug( "Invalid value specified as NR_TYPES on line: %d", no);
				return -1;
			} else {
	  			ohsm_debug("Extracted value for NR_TYPES %d", temp);
				*types = temp;
			}	
	
			xmlFree(key);
		}
		cur = cur->next->next;
	}
	if (flg_i_usr == UNSET)
		*users = 0;
	if (flg_i_grp == UNSET)
		*groups = 0;
	if (flg_i_types == UNSET)
		*types = 0;
	return 0;
}


int
parseuser (xmlDocPtr doc, xmlNodePtr cur, const unsigned int number) {

	xmlChar *key;
	int temp, no;
	int cnt_users = 0, retval;
	int uidx = 0;

	cur = cur->xmlChildrenNode;
	cur = cur->next;
	while (cur != NULL) {
		if ((!xmlStrcmp(cur->name, (const xmlChar *) "USER"))) {
		flg_usr = SET;
		cnt_users++;
			
		key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
		temp = validate_key (key);

		if (temp == -1) {
			no = (int) xmlGetLineNo(cur);
			ohsm_debug("Invalid value specified as USER on line: %d", no);
			return -1;
		}
		retval = validate_user(temp);
		if (retval) {
	  		ohsm_debug("Extracted value for USER %d", retval);
			allocpol->uid_tier[uidx].uid = temp;
		} else {
			no = (int) xmlGetLineNo(cur);
			ohsm_debug("Specifed user %d doesn't exist.", no);
		}
		xmlFree(key);
	}
	cur = cur->next;
	if ((!xmlStrcmp(cur->name, (const xmlChar *) "TIER"))) {
		key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
		temp = validate_key(key);

		if (temp == -1) {
			no = (int) xmlGetLineNo(cur);
			ohsm_debug("Invalid value specified as TIER on line: %d", no);
			return -1;
		} 
		allocpol->uid_tier[uidx++].tier = temp;
		xmlFree(key);
		}
	cur = cur->next->next;
	}

	if (cnt_users < number) {
		ohsm_debug("Number of users %d is greater than users specified %d", cnt_users, number);
		return -1;
	}
	return 0;
}

int
parsegroup(xmlDocPtr doc, xmlNodePtr cur, unsigned int groups)
{

  xmlChar *key;
  unsigned int temp_gid;
  unsigned int cnt_groups = 0;
  int retval, temp, no;
  int gidx = 0;	

  cur = cur->xmlChildrenNode;
  cur = cur->next;
  while (cur != NULL)
    {

      if ((!xmlStrcmp(cur->name, (const xmlChar *) "GROUP"))) {
		flg_grp = SET;
		cnt_groups++;
	  
	      key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	      temp = validate_key(key);

	      if (temp == -1) {
		  	no = (int) xmlGetLineNo(cur);
		  	ohsm_debug(
			   "Invalid value specified as GROUP on line: %d", no);
		  	return temp;
		  }
	      
		  retval = validate_group(temp);
	      if (!retval) {
	  		ohsm_debug("Extracted value for GROUP %d", retval);
		  	allocpol->gid_tier[gidx].gid = temp;
		  } else {
		  	no = (int) xmlGetLineNo(cur);
		  	ohsm_debug(
			   "Group ID %u doesn't exist on the system",
			    temp);
		  	return -1;			
		  }
	      xmlFree(key);
	  }
      cur = cur->next;

      if ((!xmlStrcmp(cur->name, (const xmlChar *) "TIER"))) {
	  key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	  temp = validate_key (key);

	  if (temp == -1)
	    {
	      no = (int) xmlGetLineNo(cur);
	      ohsm_debug("Invalid value specified as TIER on line: %d",
		       no);
	      return temp;
	    }
	  ohsm_debug("Extracted value for TIER %d", temp);
	  allocpol->gid_tier[gidx++].tier = temp;
	  xmlFree(key);
	}
      cur = cur->next->next;
  }
  if (cnt_groups < groups)
    {
      ohsm_debug
	("Number of groups %d is greater than groups spcified %d", cnt_groups, groups);
    }
  return 0;
}

int
parsetypes (xmlDocPtr doc, xmlNodePtr cur, unsigned int types)
{

  xmlChar *key;
  unsigned int cnt_types = 0;
  char *temp_name = NULL;
  int temp, no;
  int tidx = 0;

  cur = cur->xmlChildrenNode;
  cur = cur->next;
  while (cur != NULL)
    {

      if ((!xmlStrcmp(cur->name, (const xmlChar *) "TYPE")))
	{
	  flg_types = SET;
	  cnt_types++;

	      key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	      temp_name = (char *) key;
	      ohsm_debug("Extracted value for TYPE %s", temp_name);
	      strncpy(allocpol->type_tier[tidx].type, temp_name, MAX_EXTENSION_LENGTH);
	      xmlFree(key);
	}
   cur = cur->next->next;

      if ((!xmlStrcmp(cur->name, (const xmlChar *) "TIER")))
	{

	  key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	  temp = validate_key (key);

	  if (temp == -1)
	    {
	      no = (int) xmlGetLineNo(cur);
	      ohsm_debug("Invalid value specified as TIER on line: %d", no);
	      return temp;
	    }
	  else
	    {
	      ohsm_debug("Extracted value for TIER %d", temp);
	      allocpol->type_tier[tidx++].tier = temp;
	      xmlFree(key);
	    }
	}
      cur = cur->next->next;
    }

  if (cnt_types < types)
    {
      ohsm_debug
	("Number of types %d is greater than types spcified %d", cnt_types, types);
      return -1;
    }
  return 0;
}

int
parsedoc (char *docname)
{
	xmlDocPtr doc;
	xmlNodePtr cur;
	unsigned int users, groups, types;
	int retval;

	xmlLineNumbersDefault(1);
	doc = xmlParseFile(docname);
	if (!doc) {
		ohsm_debug("Document cannot be parsed.");
		xmlFreeDoc(doc);
	return -1;
	}

	cur = xmlDocGetRootElement(doc);
	if (cur == NULL) {
		ohsm_debug("Empty document");
		xmlFreeDoc(doc);
		return -1;
	}

	if (xmlStrcmp(cur->name, (const xmlChar *) "ALLOC_POLICY")) {
		ohsm_debug(
			"Invalid document Root element. Must be set to ALLOC_POLICY");
		xmlFreeDoc (doc);
		return -1;
	}

	cur = cur->xmlChildrenNode;
	cur = cur->next;
	while (cur != NULL) {
		if ((!xmlStrcmp(cur->name, (xmlChar *) "INFO"))) {
			retval = parseinfo(doc, cur, &users, &groups, &types);
		if (retval)
			return retval;
		allocpol->cnt_users = users;
		allocpol->cnt_groups = groups;
		allocpol->cnt_types = types;
	}

	cur = cur->next->next;
	if ((!xmlStrcmp(cur->name, (const xmlChar *) "USERS"))) {
	if (users) {
		retval = parseuser(doc, cur, users);
		if (retval)
			return retval;
		}
	}
	cur = cur->next->next;
	if ((!xmlStrcmp(cur->name, (const xmlChar *) "GROUPS"))) {
	if (groups) {
		retval = parsegroup(doc, cur, groups);
		if (retval)
			return retval;
	    }
	  }
	cur = cur->next->next;
	if ((!xmlStrcmp(cur->name,(const xmlChar *) "TYPES"))) {
		if (types) {
			retval = parsetypes(doc, cur, types);
				if (retval)
					return retval;
		}
	}
	cur = cur->next->next;
	}
	xmlFreeDoc(doc);
	return 0;
}


int
ohsm_get_allocation_policy(char *filename)
{

  int retval;
  char cmd[80];

  sprintf(cmd, "xmllint --valid --noout %s", filename);
  retval = system(cmd);
  if (!retval)
	retval = parsedoc(filename);
  else
  ohsm_debug("Lint error");
  return retval;
}
